/**
  * pow.js 
  * 
  * This is the main file invoke upon browser startup.
  * The server starts approximately 5 seconds after browser start.
  * 
  * David Kellogg, creator 
  * 
***/

// async_listener waits for a connection and responds to input from the network.
// The order is listen, accept connection, wait for 'GET', deliver payload,
// close connection.


// TODO posts over 1 MB do not work

// TODO Document REQUEST_HEADERS
// TODO Document system/startup.sjs
// TODO Document permalink.php
// TODO move system.startup.sjs
// TODO get_path.sjs for localhost only
// TODO document open and close file
// TODO Doc REQUEST_FILE
// TODO Doc SERVER_PORT
// TODO Doc REQUEST_METHOD
// TODO Doc  ajax_append, prepend


// DONE pow_session_init

/*
 *
 * Global variables. Let's keep these to a minimum.
 *
 */

//var pow_serv = null; // Object for connection

var pow_build_id = "770";
var pow_build_date = "Tue Jan  8 21:56:19 PST 2008";

var pow_serv; // Object for connection
var pow_icon_state = 0;
var pow_global_port = 0;
var pow_random_cookie = 0;
var pow_server;
var pow_wsserver;
var pow_server_active = false;

var pow_request = new Pow_Request();
var pow_response = new Pow_Response();

var pow_version = "0.1.9";
setTimeout("pow_copy_docs();", 5000);

/*
 *
 * Copies files after upgrade and first install.
 *
 */

function pow_copy_docs() {
	try {
		var version_number = pow_file("/pow/log/version_number");
		var first_time = false;
		if(navigator.userAgent.match(/Pow/)) {
			// Files must be copied from the default location for XULRunner ...
			var uuid = "";
			var def = "__DEFAULT_XR__";
			var ext = "";
		} else {
			// ... and Firefox
			var uuid = "{5776FCEA-FDBF-11DA-A132-001321F5C1D9}";
			var def = "__DEFAULT__";
			var ext = "/extensions/";
		}
		if( version_number.match(/^ERROR/) ) {
			pow_copy_file(def+ext+uuid+'/chrome/content/htdocs/index.sjs',
					'/pow/htdocs');
			first_time = true;
			pow_set_state(1);
		}
		if ( ! version_number.match(pow_version) || first_time  ) {
			pow_mkdir('/pow');
			pow_mkdir('/pow/log');
			pow_mkdir('/pow/bin');
			pow_mkdir('/pow/data');
			pow_mkdir('/pow/htdocs');
			pow_mkdir('/pow/htdocs/system');
			pow_file_put_contents("/pow/log/version_number", pow_version,"w");
			var files = new Array("pow.css","get_path.sjs","help.sjs","md5.js","favicon.ico","LICENSE.txt");
			for(var i in files) {
				pow_copy_file(def+ext+uuid+'/chrome/content/htdocs/'+files[i],
					'/pow/htdocs', true);
			}
			pow_copy_file(def+ext+uuid+
					'/chrome/content/htdocs/startup.sjs',
					'/pow/htdocs/system', false);
			if( navigator.userAgent.match(/Pow/)) {
				pow_copy_file(def+ext+uuid+
					'/chrome/content/htdocs/rpc.sjs',
					'/pow/htdocs/system', true);
				pow_copy_file(def+ext+uuid+
					'/chrome/content/htdocs/rpc.js',
					'/pow/htdocs/system', true);
			}
			pow_copy_file(def+ext+uuid+'/chrome/content/bin/makepipe.bat',
				'/pow/bin', true);
			pow_copy_file(def+ext+uuid+'/chrome/content/bin/makepipe.sh',
				'/pow/bin', true);
		}

		// EVERY TIME
		// END EVERY TIME

		if(first_time) {
			if( ! navigator.userAgent.match(/(Pow|prism)/)) {
				window.content.document.location.href = "http://localhost:6670/";
			}
		}
		if( ! version_number.match(/^ERROR/) ) {
			pow_set_state(-1);
		}
		if(! navigator.userAgent.match(/(Pow|prism|Thunderbird)/)) {
			if(gBrowser) {
				gBrowser.addEventListener("load", pow_page_load, true);
			} else {
				setTimeout("gBrowser.addEventListener('load', pow_page_load, true)", 
						10000);
			}
		}
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Object to hold originally requested file and directory.
 *
 */

function Pow_Request() {
	this.req_file = "";
	this.req_dir = "";
}

/*
 *
 * Object to keep track of responses. Refreshed for every request.
 *
 */

function Pow_Response() {
	this.script_out = "";
	this.exit_flag = false;
	this.content_type = "text/html";
	this.status_code = "200 OK";
	this.extra_headers = "";
	this.cookie = "";
	this.cookie_found = false;
	this.eval = function(command) {
		eval(command);
	}
	var document = new pow_Document();
	function include(file) { return pow_include(file); }
	function get_request_header(label) { return pow_get_request_header(label); }
	function run_ajax() { return pow_run_ajax(); }
	function get_request_header(label) { return pow_get_request_header(label); }
	function file( readfile ) { return pow_file(readfile); }
	function file_line(fstream) { return pow_file_line(fstream); }
	function file_close(fstream) { return pow_file_close(fstream); }
	function file_open(filename) { return pow_file_open(filename); }
	function file_put_contents(filename, contents, rwa_flags) { 
		return pow_file_put_contents(filename, contents, rwa_flags);
	}
	function file_delete(filename) { return pow_file_delete(filename); }
	function download_file(url) { return pow_download_file(url); }
	function file_exists(filename) { return pow_file_exists(filename); }
	function exec(file, args, blocks) { return pow_exec(file, args, blocks); }
	function file_get_data_dir() { return pow_file_get_data_dir(); }
	function file_get_log_dir() { return pow_file_get_log_dir(); }
	function file_get_docs_dir() { return pow_file_get_docs_dir(); }
	function file_list_dir(dirname) { return pow_file_list_dir(dirname); }
	function file_find_matching ( dirname , regex ) { return pow_file_find_matching(dirname, regex); }
	function upload_file(url, filename) { return pow_upload_file(url, filename); }
	function file_get_root_handle() { return pow_file_get_root_handle(); }
	function save_file (filename, contents, rwa_flags) { return save_file(filename, contents, rwa_flags) }
	function file_get_size(filename) { return pow_file_get_size(filename); }
	function file_get_handle(filename) { return pow_file_get_handle(filename); }
	function mkdir(dirname) { return pow_mkdir(dirname); }
	function file_list_dir(dirname) { pow_file_list_dir(dirname); }
}

/*
 *
 * Returns html for navigation for dummy Pow front page.
 *
 */

function get_pow_header(title) {
	try {
		var head = "<html>\n<head>\n<title>"+title;
		head += "</title>\n<link rel='stylesheet' href='pow.css' "+
			"type='text/css'>\n</head>\n<body>\n";
		head += "<a href='/'>Home</a> <a href='/help.sjs'>FAQ</a> "+
				"<a href='http://davidkellogg.com/wiki/Main_Page'>Help</a> ";
		var unique_userid = pow_bc_get_unique_userid();
		head +=
			"<a href='http://davidkellogg.com/infinilink/"+
			unique_userid+"/'>InfiniLink</a><br/>";
		return  head;
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Returns client's request header based on lower-case key.
 *
 */

function pow_get_request_header(label) {
	try {
		label = label.toLowerCase();
		if(pow_server.REQUEST_HEADERS[label]) {
			return pow_server.REQUEST_HEADERS[label];
		} else {
			return "";
		}
	} catch(e) {
		log_error(e); return "";
	}
}

/*
 *
 * Rarely used ajax helper. Should not use pow_server directly.
 *
 */

function pow_run_ajax() {
		var text_out = "";
 	 	if(pow_server.GET['AJAX'] == 'true') {
			try {
				pow_clear();
 	  		var new_ar;
 	 			if(pow_server.GET['action'] == 'append') {
 	  			new_ar = pow_server.append_text();
 	 			} else if(pow_server.GET['action'] == 'replace') {
 	  			new_ar = pow_server.put_text();
				} else {
 	  			new_ar = pow_server.put_text();
				}
 	  		pow_response.script_out += 
				"var RESPONSE = { element: '"+new_ar[0]+"', val: \""+new_ar[1]+"\"}\n";
			} catch(e) {
				log_error(e); return "";
			}
			pow_exit();
 	 	} else {
			try {
 	   		pow_response.script_out += "<html>\n<body>\n";
 	   		pow_response.script_out += "<script>var send_xhr=true;</script>\n";
				pow_server.xhr = pow_server.print_xhr();
				pow_response.script_out += pow_server.xhr;
			} catch(e) {
				log_error(e); return "";
			}
 	 	}
}

/*
 *
 * Evaluates a separate sjs file.
 * The file must contain <?sjs ?> markup.
 *
 */

function pow_include(filename) {
	try {
		var contents = "Content-Location: "+filename+"\r\n";
		var jsfile = pow_file(filename);
		if(filename.match(/\.js$/)) {
			// We do not assume js files will or will not have <?sjs
			jsfile = jsfile.replace(/<\?sjs/m,"");
			jsfile = jsfile.replace(/\?>/m,"");
			jsfile = "<?sjs "+jsfile+"?>";
		}
		contents += jsfile;
		contents = pow_server.process_server_js(contents);
		contents = contents.replace(/Content-Location: .*\r\n/m,"");
		pow_response.script_out += contents;
	} catch (e) {
		log_error(e); return "";
	}
}

/*
 *
 * Prints pow_server variables.
 *
 */

function pow_info() {
	try {
		var info = "";
		info += "<table border='1' >";
		var request_text = pow_array_tostring(pow_server.REQUEST);
		info += "<tr><td>pow_server.REQUEST</td><td>"+
				request_text+"</td></tr>";
		var post_text = pow_array_tostring(pow_server.POST);
		info += "<tr><td>pow_server.POST</td><td>"+post_text+"</td></tr>";
		var get_text = pow_array_tostring(pow_server.GET);
		info += "<tr><td>pow_server.GET</td><td>"+get_text+"</td></tr>";
		info += "<tr><td>pow_server.REMOTE_HOST</td><td>"+
				pow_server.REMOTE_HOST+"</td></tr>";
		info += "<tr><td>pow_server.POST_FILENAME</td><td>"+
				pow_server.POST_FILENAME+"</td></tr>";
		info += "</table>";
		pow_response.script_out += info;
	} catch (e) {
		log_error(e); return "";
	}
}

/*
 *
 * Returns standard footer for Pow pages.
 *
 */

function get_pow_footer() {
	return "\n</body>\n</html>";
}

/*
 *
 * Listens for page unloads.
 * Starts pow listener after failure. This is for breadcrumbs, only.
 *
 */

function restart_pow_listener() {
	try {
		var d = window.content.document;
		var head = d.getElementsByTagName("head")[0];
		if( ! pow_is_blacklisted(d.location.href)) {
			save_pow_body(d.location.href, d.title, head.innerHTML, d.body.innerHTML);
		}
		window.content.addEventListener("unload", found_pow_unload, true ); 
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Adds label and startup script for a module.
 *
 */

function pow_register_startup(name, init_file) {
  var confirmed = false;
  if(name && init_file) {
    confirmed = confirm("The module "+name+
			" would like to install a startup script, "+
			init_file+"\nWould you like to do this?");
  }
  if(confirmed) {
    var startup = pow_file("/pow/htdocs/system/startup.sjs");
    if(! startup.match(name+":")) {
      startup = startup.replace("?"+">", name+
				":\npow_include\('/pow/htdocs/modules/"+name+
				"/"+init_file+"'\);\n?"+">");
      pow_file_put_contents("/pow/htdocs/system/startup.sjs",startup);
    }
  }
}


/*
 *
 * Handles page load events to save data.
 *
 */

function pow_page_load(event) {
  if (event.originalTarget instanceof HTMLDocument) {
    if (event.originalTarget.defaultView.frameElement) {
      var doc = event.originalTarget;
      while (doc.defaultView.frameElement) {
        doc=doc.defaultView.frameElement.ownerDocument;
      }
    } else {
      var doc = event.originalTarget;
    }
  }
	if(!doc) { return; }
	if(doc.location.href.match(/\.install\.(sjs|html)$/)) {
		var inner = doc.body.innerHTML;
		var m  = inner.match(/<script>([^<>]|\n|\r)*var pow_installer = ({([^<>]|\r|\n)+});([^<>]|\r|\n)*<\/script>/);
		if(m) {
			var json = m[2];
			var m2 = json.match(/location:\s*"(http:[^"]+)/);
			var m3 = json.match(/name:\s*"([a-zA-Z_]+)/); //"
			var m4 = json.match(/version:\s*"([0-9\.]+)/); //"
			var m5 = json.match(/startup:\s*"([a-zA-Z_\.]+)/); //"
			var location = m2[1];
			var name = m3[1];
			var version = m4[1];
			var startup_script = m5[1];
			var confirmed = false;
			if(location && location.match(/\.zip$/) && name && version) {
				var confirmed = confirm("Do you want to install the POW module "+
					name+"?");
			}
			if( confirmed ) {
				var zip = pow_download_file(location);
				pow_file_put_contents("/pow/modules/staged.zip",zip,"w");
				var fh = pow_file_get_handle("/pow/modules/staged.zip");
				var unzip = Components.classes["@mozilla.org/libjar/zip-reader;1"]
 	     		 .createInstance(Components.interfaces.nsIZipReader);
				unzip.init(fh);
				unzip.open();
				var enum = unzip.findEntries("*.*");
				pow_file_put_contents("/pow/htdocs/modules/"+name+"/version.json",
						'{ "version": "'+version+'", "name": "'+name+'" }\n',"w");

				while(enum.hasMoreElements()) {
					var entry = 
							enum.getNext().QueryInterface(Components.interfaces.nsIZipEntry);
					if(name && entry.name.match(/\.(sjs|html|js|gif|png|jpg|sqlite|xml)$/)) {
						var fh_out = 
								pow_file_get_handle("/pow/htdocs/modules/"+name+"/"+entry.name);
							unzip.extract(entry.name, fh_out);
					}
				}
				unzip.close();
				if(startup_script) {
					pow_register_startup(name, startup_script);
				}
			}
		}
	}
}

/**
  *
  * Sets the state of the icon in FF to gray/colored
  *
  */

function pow_set_state(new_state) {
	try {
		if(! navigator.userAgent.match(/(Pow|prism)/)) {
			this.statusImage = document.getElementById("pow-status-image");
			var known_state = true;
		}
    if(new_state == -1) { // Go to last known value
			if(! navigator.userAgent.match(/(Pow|prism)/)) {
      	known_state = false;
			}
			pow_icon_state = pow_get_icon_state();
			new_state = pow_get_icon_state();
    }
		if(new_state == 0 || new_state == 2) {
			if(! navigator.userAgent.match(/(Pow|prism)/)) {
				this.statusImage.src = "chrome://pow/skin/fire_sg.gif";
				this.statusImage.tooltipText = "POW disabled";
			}
			pow_server.serv.close();
			pow_server_active = false;
			pow_icon_state = 0;
		} else if(new_state == 1 ) {
			if(! navigator.userAgent.match(/(Pow|prism|Thunderbird)/)) {
				this.statusImage.src = "chrome://pow/skin/fire_s.gif";	
				this.statusImage.tooltipText = "POW enabled";
			}
			if(! pow_server_active) {
				pow_global_port = pow_bc_get_port_number();
				pow_server = new pow_Serv();
				var success = pow_server.start_server(pow_global_port);
				if(success) {
					pow_server_active = true;
					pow_icon_state = 1;
					pow_server.BUILD_ID=pow_build_id+" "+pow_build_date;
				} else {
					if(! navigator.userAgent.match(/(Pow|prism|Thunderbird)/)) {
						this.statusImage.src = "chrome://pow/skin/fire_sg.gif";
						this.statusImage.tooltipText = "POW disabled";
					}
				}
			}
			/*
			if(! navigator.userAgent.match(/Pow/)) {
				if(known_state) {
					// NECESSARY?
					pow_last_visited = window.content.document.location.href;
				}
			}
			*/
		}
		pow_set_icon_state(pow_icon_state);
	} catch(e) {
		log_error(e);
	}

}

/*
 *
 * Sends current window to http://localhost:6670/
 *
 */

function pow_send_browser_home() {
	try {
		window.content.document.location.href = 
			"http://localhost:"+pow_server.SERVER_PORT+"/";
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Toggles server on/off from icon.
 *
 */

function pow_rotate_state() {
	try {
		pow_icon_state++;
		if(pow_icon_state > 1) {
			pow_icon_state = 0;
		}
		pow_set_state(pow_icon_state);
	} catch(e) {
		log_error(e);
	}
}

/*
 *
 * Deletes filename. If recursive is set to true, it recursively deletes all.
 *
 */

function delete_pow_file (filename, recursive) {
	try {
		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
  } catch (e) {
     log_error("Permission to delete file was denied.");
  }
	try {
	var serv_dir_file = filename;
	var f =  pow_file_get_handle("/");
	f = append_pow_path(f, serv_dir_file);
  if ( f.exists() == false ) {
		return;
	}
	f.remove(recursive);
  } catch (e) {
     log_error(e);
  }
}

/**
 *
 * Add several directories
 * This will append to the path
 *
 */

function append_pow_path (file, path) {
	var dirs = new Array();
	var num = 0;
	while( path.match(/^[^\/]*\/.* ?/) ) {
		var next_dir = path.replace(/^([^\/]*)\/(.*)/,"$1");
		file.append(next_dir);
		path = path.replace(/^([^\/]*)\/(.*)/,"$2");
	}
	if ( path ) {
		file.append(path);
	}
	return file;
}

/*
 *
 * Closes server
 *
 */

function close_pow() {
	pow_serv.close();
}

/*
 *
 * Returns pow login form. Client should receive cookie upon success.
 *
 */

function get_pow_login_form( last_url ) {
	var form = "<form method='POST' action='"+last_url+"'>\n";
	form += "User ID:  <input type='text' name='userid'  size=20 /><br>\n";
	form += "Password: <input type='text' name='password'  size=20 />\n";
	form += "<input type='submit' value='Login' />\n";
	form += "</form><br>";
	return form;
}

/*
 *
 * Returns breadcrumbs search form.
 *
 */

function get_pow_form( query ) {
	if(query) {
		query = query.replace(/\+/g, " ");
	} else {
		query = '';
	}
	var form = "<form method='GET' action='/htdocs/search.sjs'>\n";
	form += "<input type='text' name='query' value='"+query+"' size=20 />\n";
	form += "<input type='submit' value='Search' />\n";
	form += "</form><br>";
	return form;
}

